package com.warren.system.aspect;

import com.alibaba.fastjson.JSON;
import com.warren.annotation.ControllerEndpoint;
import com.warren.auth.entity.JwtUser;
import com.warren.system.entity.Log;
import com.warren.system.service.LogService;
import com.warren.util.AddressUtils;
import com.warren.util.IpUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;

/**
 * @author warren
 * @create 2021-04-18 19:47
 * @Description  系统日志切面
 */
@Slf4j
@Aspect
@Component
public class ControllerEndpointAspect extends AspectSupport{

    private final Log sysLog = new Log();

    @Resource
    private LogService logService;

    /**
     * 切入方法
     */
    @Pointcut("@annotation(com.warren.annotation.ControllerEndpoint)")
    public void pointcut(){

    }

    /**
     * 环绕通知
     * @param joinPoint 切点类
     * @return 保存的日志
     * @throws Throwable
     */
    @Around("pointcut()")
    public Object saveSysLog(ProceedingJoinPoint joinPoint) throws  Throwable{
        Object result = null;

        //开始时间
        long startTime = System.currentTimeMillis();

        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        Method method = signature.getMethod();

        //获取注解
        ControllerEndpoint controllerEndpoint = method.getAnnotation(ControllerEndpoint.class);
        if (controllerEndpoint != null){
            String operation = controllerEndpoint.operation();
            //注解上的操作描述
            sysLog.setOperation(operation);
        }

        //请求的参数
        Object[] args = joinPoint.getArgs();
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        String[] parameterNames = u.getParameterNames(method);
        sysLog.setParams("paramName:" + Arrays.toString(parameterNames) + ",args:"+Arrays.toString(args));

        //请求的ip
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String ipAdrr = IpUtils.getIpAddr(request);
        sysLog.setIp(ipAdrr);

        //地理位置
        String address = AddressUtils.getRealAddressByIP(ipAdrr);
        sysLog.setLocation(address);

        //操作人
        //获取security当前用户信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (!(authentication instanceof AnonymousAuthenticationToken)){
            JwtUser userInfo = (JwtUser) authentication.getPrincipal();
            sysLog.setUsername(userInfo.getId() + userInfo.getUsername());
        }

        //添加时间
        //sysLog.setCreateTime(new Date());

        //执行目标
        result = joinPoint.proceed();

        //请求的方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        System.out.println(request);

        sysLog.setMethod(className + "," + methodName + "()\n" +"\nresponse:" + postHandle(result));

        //执行消时
        sysLog.setTime(System.currentTimeMillis() - startTime);
        //保存系统日志
        logService.saveLog(sysLog);
        return result;
    }

    private String postHandle(Object retVal){
        if (null == retVal){
            return "";
        }
        return JSON.toJSONString(retVal);
    }
}
